<template>
  <div ref="v3_viewer__container">
    <slot :images="images" :options="options"></slot>
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  inject,
  ref,
  onMounted,
  onUnmounted,
  WatchEffect,
  watch,
  nextTick,
  computed,
} from "vue";
import Viewer from "viewerjs";
import { ViewerSymbol } from "../utils/constant";

export default defineComponent({
  name: "v3-viewer",
  props: {
    images: {
      type: Array,
      required: true,
    },
    rebuild: {
      type: Boolean,
      default: false,
    },
    trigger: {},
    options: {
      type: Object,
      default: {},
    },
  },
  setup(props, context) {
    const $viewer = inject(
      ViewerSymbol,
      ref<Viewer | null>(null) /* optional default value */
    );
    const v3_viewer__container = ref<HTMLElement | null>(null);

    // 初始options
    const initialOptions = {
      backdrop: true, // 有蒙层
      inline: false, // inline模式:插入到container的后面,并且覆盖在其上层;modal模式
      button: true, // 关闭
      /*{
          0 or false: hide the navbar
          1 or true: show the navbar
          2: show the navbar only when the screen width is greater than 768 pixels
          3: show the navbar only when the screen width is greater than 992 pixels
          4: show the navbar only when the screen width is greater than 1200 pixels
        }*/
      navbar: 3, // 缩略图
      /*{
          0或false：隐藏标题
          1或true或Function或Array：显示标题
          2：仅在屏幕宽度大于768像素时显示标题
          3：仅在屏幕宽度大于992像素时显示标题
          4：仅在屏幕宽度大于1200像素时显示标题
          Function：自定义标题内容
          [Number, Function]：第一个元素指示可见性，第二个元素自定义标题内容
        }*/
      // title?: boolean | Visibility | Function | [Visibility, Function]; // 默认显示url链接和尺寸,
      /*{
          0 or false: hide the toolbar.
          1 or true: show the toolbar.
          2: show the toolbar only when the screen width is greater than 768 pixels.
          3: show the toolbar only when the screen width is greater than 992 pixels.
          4: show the toolbar only when the screen width is greater than 1200 pixels.
          { key: Boolean | Number }: show or hide the toolbar.
          { key: String }: customize the size of the button.
          { key: Function }: customize the click handler of the button.
          { key: { show: Boolean | Number, size: String, click: Function }: customize each property of the button.
          Available keys: "zoomIn", "zoomOut", "oneToOne", "reset", "prev", "play", "next", "rotateLeft", "rotateRight", "flipHorizontal" and "flipVertical".
          Available sizes: "small", "medium" (default) and "large".
        }*/
      toolbar: true, // 操作栏
      tooltip: true, // 缩放时显示百分比例的提示
      movable: true, // 可移动图片
      zoomable: true, // 可缩放图片
      zoomOnTouch: true, // 启用两指缩放当前图像
      zoomOnWheel: true, // 启用可通过鼠标滚轮缩放图像
      slideOnTouch: true, // 在触摸屏上滑动切换上下
      toggleOnDblclick: true, // 双击图像时是否在图像的自然尺寸和初始尺寸之间切换
      rotatable: true, // 可旋转图片
      scalable: true, // 可翻折图片
      transition: true, // 显隐动画
      fullscreen: true, // Enable to request full screen when play.
      /*{
          Esc: Exit full screen or close the viewer or exit modal mode or stop play.
          Space: Stop play.
          Tab: Switch the focus state on the buttons in the viewer.
          Enter: Trigger the click event handler on the button.
          ←: View the previous image.
          →: View the next image.
          ↑: Zoom in the image.
          ↓: Zoom out the image.
          Ctrl + 0: Zoom out to initial size.
          Ctrl + 1: Zoom in to natural size.
        }*/
      keyboard: true, // 键盘
      url: "data-source", // [String或Function]url从容器内img标签的那个属性获取(默认src)
      className: "",
      container: "body",
      filter: undefined,
      initialViewIndex: 0, // 初始索引
      // 定义要从原始图像继承的额外属性
      inheritedAttributes: [
        "crossOrigin",
        "decoding",
        "isMap",
        "loading",
        "referrerPolicy",
        "sizes",
        "srcset",
        "useMap",
      ],
      interval: 3000, // 播放时自动循环播放图像之间延迟的时间(默认5000)
      loading: true, // spinner
      loop: true, // 循环 first & last
      minWidth: 200, // 定义查看器的最小宽度(inline模式下生效),
      minHeight: 100, // 定义查看器的最小高度(inline模式下生效),
      zIndex: 2015,
      zIndexInline: 0, // inline模式下的`z-index`
      zoomRatio: 0.1, // 滚轮鼠标每次4%变化
      minZoomRatio: 0.01, // 定义缩小时图像的最小比例
      maxZoomRatio: 100, // 定义放大时图像的最大比例
      // methods...
    };
    // 综合options = 初始options + props.options
    const synthesisOptions = computed<
      | typeof initialOptions
      | {
          title: [number, Function];
        }
    >(() => {
      return {
        ...initialOptions,
        title: [
          // 默认显示url链接和尺寸
          4,
          (image: any, imageData: any) => {
            return `[title] ${image.alt} (${imageData.naturalWidth} × ${imageData.naturalHeight})`;
          },
        ],
        ...props.options,
      };
    });

    /**
     * 创建Viewer
     */
    const createViewer = () => {
      if (!v3_viewer__container.value) {
        console.error("no container", v3_viewer__container.value);
        return;
      }
      $viewer.value = new Viewer(v3_viewer__container.value, synthesisOptions.value);
      context.emit("inited", $viewer.value);
    };
    /**
     * 销毁Viewer
     */
    const destroyViewer = () => {
      $viewer.value && $viewer.value.destroy();
    };
    /**
     * 更新Viewer: 图片出现变化(增删改...)
     */
    const updateViewer = () => {
      if ($viewer.value) {
        $viewer.value.update();
        context.emit("inited", $viewer.value);
      } else {
        createViewer();
      }
    };
    /**
     * 重建(先销毁再创建)Viewer
     */
    const rebuildViewer = () => {
      destroyViewer();
      createViewer();
    };
    /**
     * 根据props.rebuild的布尔值在变化时决定: 是重建还是更新
     */
    const onChange = () => {
      if (props.rebuild) {
        rebuildViewer();
      } else {
        updateViewer();
      }
    };
    onMounted(() => {
      createViewer();
    });
    onUnmounted(() => {
      destroyViewer();
    });
    /**
     * 资源有变化: 执行onChange,视情况决定是重建还是更新
     * 
     * 可以是getter/effect函数、ref、Proxy以及它们的数组。绝对不可以是纯对象或基本数据。
     */
    watch(
      [props.images, props.trigger],
      () => {
        nextTick(() => {
          onChange();
        });
      },
      { deep: true }
    );
    /**
     * 配置改变,直接选择重建
     */
    watch(
      props.options,
      () => {
        nextTick(() => {
          rebuildViewer();
        });
      },
      {
        deep: true,
        //  immediate: true
      }
    );

    return {
      v3_viewer__container,
    };
  },
});
</script>

<style>
@import "viewerjs/dist/viewer.css";
</style>
